Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed last year.
Improve this question
I need to update my cpp source files header by adding extra one space before starting of the below lines in header information -
This file is an example header file.
There are 100 files are present with this formats.
Some files are with proper space before starting of each line in this paragraph.
How to move each line in this paragraph with single space at start using any script.
All above lines are fixed throughout all files.
Present Header format:
/*++++++++++++++++++++++++++++++ FileHeaderBegin +++++++++++++++++++++++++++++++
ALL INFORMATION ARE BELONGS TO ABCDEF
Copyright 2015 - 2020 ABCDEF LTD
All Rights Reserved
This file is an example header file.
There are 100 files are present with this formats.
Some files are with proper space before starting of each line in this paragraph.
How to move each line in this paragraph with single space at start using any script.
All above lines are fixed throughout all files.
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* FILENAME: test.cpp
*
* DESCRIPTION: Sample file for command test
*
*++++++++++++++++++++++++++++++ FileHeaderEnd +++++++++++++++++++++++++++++++*/
Expected:
/*++++++++++++++++++++++++++++++ FileHeaderBegin +++++++++++++++++++++++++++++++
ALL INFORMATION ARE BELONGS TO ABCDEF
Copyright 2015 - 2020 ABCDEF LTD
All Rights Reserved
This file is an example header file.
There are 100 files are present with this formats.
Some files are with proper space before starting of each line in this paragraph.
How to move each line in this paragraph with single space at start using any script.
All above lines are fixed throughout all files.
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* FILENAME: test.cpp
*
* DESCRIPTION: Sample file for command test
*
*++++++++++++++++++++++++++++++ FileHeaderEnd +++++++++++++++++++++++++++++++*/
Query:
Is there any command or script available to do that changes.
I have total 270 files with few files with proper space added and majority files with no space added.
This might work for you (GNU sed):
sed -i '/FileHeaderBegin/{:a;n;s/^\S/ &/;/FileHeaderEnd/!ba}' file1 ... filen
Turn on edit inplace option -i
Insert a space at the front of any line that begins with a non-space character between the lines containing FileHeaderBegin and FileHeaderEnd.
Alternative solution:
sed -i '/FileHeaderBegin/,/FileHeaderEnd/s/^\S/ &/' file1 ... filen
Using sed
$ sed '/^[[:space:]]/ ! s/^/ /' input_file
/*++++++++++++++++++++++++++++++ FileHeaderBegin +++++++++++++++++++++++++++++++
ALL INFORMATION ARE BELONGS TO ABCDEF
Copyright 2015 - 2020 ABCDEF LTD
All Rights Reserved
This file is an example header file.
There are 100 files are present with this formats.
Some files are with proper space before starting of each line in this paragraph.
How to move each line in this paragraph with single space at start using any script.
All above lines are fixed throughout all files.
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* FILENAME: test.cpp
*
* DESCRIPTION: Sample file for command test
*
*++++++++++++++++++++++++++++++ FileHeaderEnd +++++++++++++++++++++++++++++++*/
If your file header always looks like that you can do the following with GNU awk:
awk '(FNR==1){h=0}(!h && !/^ /){$0=" " $0}/*\//{h++}1' file
You can now put this in a loop to process your files.
This awk script searches for the string "*/" which appears for the first time right after the header. Only until this string is found, we check for lines that mis the space at the beginning.
For inplace modifications with awk: see Save modifications in place with awk
I just need to move a line up in sed. I can select the line with
sed -i '7s///'
I need to move line 7 up 2 lines so it will be line 5.
I can't find anything on the internet to do this without complicated scripts, I can't find a simple solution of moving a specific line a specific number of times.
seq 10|sed '5{N;h;d};7G'
when up to line 5 append next line(line 6) into pattern space then save them into hold space and delete them from pattern space; up to line 7 then append the hold space content("5\n6") behind the line 7; now, pattern space is "7\n5\n6";finally,sed will print the pattern space at the end of current cycle by default(if no "-n" parameter)
ed is better at this, since it has a "move" command that does exactly what you want. To move line 7 to be the line after line 4, just do 7m4. ed doesn't write the data back by default, so you need to explicitly issue a w command to write the data:
printf '7m4\nw\n' | ed input
Although it is perhaps better to use a more modern tool:
ex -s -c 7m4 -c w -c q input
Such as title, I want to copy all lines and paste at the end.
BEFORE:
apple
cat
dog
sun
AFTER:
apple apple
cat cat
dog dog
sun sun
Use a substitue command
:%s/.*/& &
Where .* matches everything and & is replaced with the match (in this case the whole line)
Or if you really want to yank the lines you could use a normal command
:%norm yyPJ
Which is run the command yyPJ on every line in normal mode.
Note: These commands will give slightly different output if there is leading whitespace.
If you're on unix-like system:
:%!paste -d' ' % -
Another unix-style answer (though I would go with any of #FDinoff's solution):
:%!awk '{print $1, $1}'
And another :normal answer because there's so many ways to enjoy skinning a cat:
:%norm y$A <C-v><C-r>"
And another one:
:%norm y$Pa<space> <-- just press the <space> bar
Another way , if your text is like this(~ represents blank)
apple
cat~~
dog~~
sun~~
It means that all word has the same numbers of colomn.
you can ctrl-v to select all and move cursor to the end of "apple" and type p to paste.
Here's an output of diff -u "temp temp/docs 1.txt" "temp temp/docs 2.txt":
--- temp temp/docs 1.txt Mon Apr 7 16:15:08 2014
+++ temp temp/docs 2.txt Mon Apr 7 16:18:45 2014
## -2,6 +2,6 ##
22
333
4444
-555555
+55555
666666
7777777
However, feeding this diff to patch -u fails with following message:
can't find file to patch at input line 3
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|--- temp temp/docs 1.txt Mon Apr 7 16:15:08 2014
|+++ temp temp/docs 2.txt Mon Apr 7 16:18:45 2014
--------------------------
Apparently, the spaces are the problem; is there a way to make patch to work on files with spaces in names?
No, GNU patch doesn't support this. Here's the official statement: http://www.gnu.org/software/diffutils/manual/html_node/Unusual-File-Names.html#Unusual%20File%20Names
Gnu patch 2.6.1 (linux) seems to obey at least 1 space (not tried with more) if the filename is separated from the date with tab.
YYMV
I encountered the same problem when trying to establish conventions how to do manual version control with diff and patch.
I found out that GNU "diff" creates quoted path names in the patch headers if they contain spaces, while BusyBox "diff" doesn't.
Neither GNU nor BusyBox "patch" accepts quoted path names.
If the problem is just embedded spaces within filenames, it can therefore be avoided by using "busybox patch" rather than GNU "patch".
Another solution is to postprocess the output of GNU "diff" before feeding it into "patch":
sed 's,^\([-+]\{3\} \)"\([^"]*\)",\1\2,' $PATCHFILE | patch -p1
This works whether $PATCHFILE was created with GNU or busybox diff, but will only work with unified diff format.
Unfortunately, it turns out that leading or trailing spaces in filenames cannot be preserved with this method, as "patch" will skip them when parsing the path names from the patch instructions.
The approach will neither work if the filename starts with a literal double quote - but then, who uses such file names?
Most of the time, however, the above approach works just fine.
Finally a note of other approaches I have also tried but which did not work:
First I tried to replace the quotation of the whole path names by individually quoted path name components. This failed because "patch" does not use double quotes as meta-characters at all. It considers them to be normal literal characters.
Then I tried to replace all spaces by "\040" like CVS does - but "patch" does not seem to accept octal-escapes either, and this failed too.
I'm trying to do my homework that is restricted to only using sed to filter an input file to a certain format of output. Here is the input file (named stocks):
Symbol;Name;Volume
================================================
BAC;Bank of America Corporation Com;238,059,612
CSCO;Cisco Systems, Inc.;28,159,455
INTC;Intel Corporation;22,501,784
MSFT;Microsoft Corporation;23,363,118
VZ;Verizon Communications Inc. Com;5,744,385
KO;Coca-Cola Company (The) Common;3,752,569
MMM;3M Company Common Stock;1,660,453
================================================
And the output needs to be:
BAC, CSCO, INTC, MSFT, VZ, KO, MMM
I did come up with a solution, but it's not efficient. Here is my sed script (named try.sed):
/.*;.*;[0-9].*/ { N
N
N
N
N
N
s/\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*/\1, \2, \3, \4, \5, \6, \7/gp
}
The command that I run on shell is:
$ sed -nf try.sed stocks
My question is, is there a better way of using sed to get the same result? The script I wrote only works with 7 lines of data. If the data is longer, I need to re-modify my script. I'm not sure how I can make it any better, so I'm here asking for help!
Thanks for any recommendations.
One more way using sed:
sed -ne '/^====/,/^====/ { /;/ { s/;.*$// ; H } }; $ { g ; s/\n// ; s/\n/, /g ; p }' stocks
Output:
BAC, CSCO, INTC, MSFT, VZ, KO, MMM
Explanation:
-ne # Process each input line without printing and execute next commands...
/^====/,/^====/ # For all lines between these...
{
/;/ # If line has a semicolon...
{
s/;.*$// # Remove characters from first semicolon until end of line.
H # Append content to 'hold space'.
}
};
$ # In last input line...
{
g # Copy content of 'hold space' to 'pattern space' to work with it.
s/\n// # Remove first newline character.
s/\n/, /g # substitute the rest with output separator, comma in this case.
p # Print to output.
Edit: I've edited my algorithm, since I had neglected to consider the header and footer (I thought they were just for our benefit).
sed, by its design, accesses every line of an input file, and then performs expressions on ones that match some specification (or none). If you're tailoring your script to a certain number of lines, you're definitely doing something wrong! I won't write you a script since this is homework, but the general idea for one way to go about it is to write a script that does the following. Think of the ordering as the order things should be in a script.
Skip the first three lines using d, which deletes the pattern space and immediately moves on to the next line.
For each line that isn't a blank line, do the following steps. (This would all be in a single set of curly braces.)
Replace everything after and including the first semicolon (;) with a comma-and-space (", ") using the s (substitute) command.
Append the current pattern space into the hold buffer (look at H).
Delete the pattern space and move on to the next line, like in step 1.
For each line that gets to this point in the script (should be the first blank line), retrieve the contents of the hold space into the pattern space. (This would be after the curly braces above.)
Substitute all newlines in the pattern space with nothing.
Next, substitute the last comma-and-space in the pattern space with nothing.
Finally, quit the program so you don't process any more lines. My script worked without this, but I'm not 100% sure why.
That being said, that's just one way to go about it. sed often offers varying ways of varying complexity to accomplish a task. A solution I wrote with this method is 10 lines long.
As a note, I don't bother suppressing printing (with -n) or manually printing (with p); each line is printed by default. My script runs like this:
$ sed -f companies.sed companies
BAC, CSCO, INTC, MSFT, VZ, KO, MMM
This sed command should produce your required output:
sed -rn '/[0-9]+$/{s/^([^;]*).*$/\1/p;}' file.txt
OR on Mac:
sed -En '/[0-9]+$/{s/^([^;]*).*$/\1/p;}' file.txt
This might work for you:
sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;q};d' stocks
We don't want the headings so let's delete them. 1d
All data items are delimited by ;'s so let's concentrate on those lines. /;/
Of the things above delete everything from the first ; to the end of line and then stuff it away in the the hold space (HS) {s/;.*//;H}
When you get to the last line, overwrite it with the HS using the g command, delete the first newline (generated by the H command), replace all subsequent newlines with a comma and a space and print out what's left. ${g;s/.//;s/\n/, /g;q}
Delete everything else d
Here's a terminal session showing the incremental refinement of building a sed command:
cat <<! >stock # paste the file into a here doc and pass it on to a file
> Symbol;Name;Volume
> ================================================
>
> BAC;Bank of America Corporation Com;238,059,612
> CSCO;Cisco Systems, Inc.;28,159,455
> INTC;Intel Corporation;22,501,784
> MSFT;Microsoft Corporation;23,363,118
> VZ;Verizon Communications Inc. Com;5,744,385
> KO;Coca-Cola Company (The) Common;3,752,569
> MMM;3M Company Common Stock;1,660,453
>
> ================================================
> !
sed '1d;/;/!d' stock # delete headings and everything but data lines
BAC;Bank of America Corporation Com;238,059,612
CSCO;Cisco Systems, Inc.;28,159,455
INTC;Intel Corporation;22,501,784
MSFT;Microsoft Corporation;23,363,118
VZ;Verizon Communications Inc. Com;5,744,385
KO;Coca-Cola Company (The) Common;3,752,569
MMM;3M Company Common Stock;1,660,453
sed '1d;/;/{s/;.*//p};d' stock # delete all non essential data
BAC
CSCO
INTC
MSFT
VZ
KO
MMM
sed '1d;/;/{s/;.*//;H};${g;l};d' stock # use the l command to see what's really there!
\nBAC\nCSCO\nINTC\nMSFT\nVZ\nKO\nMMM$
sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;l};d' stock # refine refine
BAC, CSCO, INTC, MSFT, VZ, KO, MMM$
sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;q};d' stock # all done!
BAC, CSCO, INTC, MSFT, VZ, KO, MMM