Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question?
If it is not in the man pages or the how-to's this is the place! |
Notices |
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
Are you new to LinuxQuestions.org? Visit the following links:
Site Howto |
Site FAQ |
Sitemap |
Register Now
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
|
|
06-20-2012, 11:50 AM
|
#1
|
LQ Newbie
Registered: Feb 2011
Posts: 5
Rep:
|
Searching and replacing strings in groups of files
I realize that this has been covered numerous times in the past, but I can't seem to get any of the previous suggestions to work. I've tried various things with grep and sed.
I am trying to replace a string of text "line-width = 6 \in"
with the string "line-width = 7.8 \in"
in 500+ lilypond files so that they display better on my nook. The problem seems to be the backslash, but trying it with a double backslash doesn't improve things.
Thank you.
|
|
|
06-20-2012, 11:59 AM
|
#2
|
Member
Registered: Sep 2008
Location: The Netherlands
Distribution: Slackware64 current
Posts: 594
Rep:
|
Replace 6 with 7.8
Code:
bash-4.2$ echo "line-width = 6 \in" | sed 's/6/7.8/'
line-width = 7.8 \in
|
|
|
06-20-2012, 12:07 PM
|
#3
|
LQ Guru
Registered: May 2005
Location: Atlanta Georgia USA
Distribution: Redhat (RHEL), CentOS, Fedora, CoreOS, Debian, FreeBSD, HP-UX, Solaris, SCO
Posts: 7,831
|
Sometimes you need to break the problem into pieces. You can use fgrep without the backslash but with double quotes to find the pattern in your files. You can then use sed with backslashes to esccape the lines and make them come out right:
Code:
for file in $(fgrep -l "6 \in" *)
do echo $file
sed -e 's/6 \\in/7.8 \\in/g' $file >new$file
mv new$file $file
done
The -l tells fgrep to simply list files that match. The loop will make the change for every file found by fgrep.
|
|
1 members found this post helpful.
|
06-21-2012, 11:39 AM
|
#4
|
Bash Guru
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852
|
The best thing to do is try to avoid the problem area entirely. You don't necessarily need to use the whole string in the sed command.
Code:
sed '/line-width/ s/= 6/= 7.8/'
This first finds lines that contain the phrase "line-width", then replaces "= 6" with "= 7.8" in those lines.
In fact, if the text you want to change is all on a single line, with nothing else that needs to be preserved, why not just replace the entire line?
Code:
sed '/line-width/ c line-width = 7.8'
Edit: BTW, you can use globbing patterns to feed sed multiple files at once, and the -i option to edit them in place. Be sure not to enable it until you've thoroughly tested your solution, of course.
Last edited by David the H.; 06-21-2012 at 11:49 AM.
Reason: as written
|
|
1 members found this post helpful.
|
06-21-2012, 11:45 AM
|
#5
|
Bash Guru
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852
|
Quote:
Originally Posted by MensaWater
Code:
for file in $(fgrep -l "6 \in" *)
do
|
You oughta know by now... Don't Read Lines With For!
|
|
|
06-22-2012, 01:07 AM
|
#6
|
LQ Newbie
Registered: Feb 2011
Posts: 5
Original Poster
Rep:
|
Even though I shouldn't have read the line with 'for' it solved my problem.
Thank you.
|
|
|
06-22-2012, 08:40 AM
|
#7
|
LQ Guru
Registered: May 2005
Location: Atlanta Georgia USA
Distribution: Redhat (RHEL), CentOS, Fedora, CoreOS, Debian, FreeBSD, HP-UX, Solaris, SCO
Posts: 7,831
|
Quote:
Originally Posted by David the H.
|
for has worked for years for me and no matter how often you post this I'll likely continue doing it.
|
|
|
06-22-2012, 08:43 AM
|
#8
|
LQ Guru
Registered: May 2005
Location: boston, usa
Distribution: fedora-35
Posts: 5,326
|
Quote:
Originally Posted by MensaWater
for has worked for years for me and no matter how often you post this I'll likely continue doing it.
|
i do that too, but:
Code:
fgrep -l "6 \in" * | while read line
...
would handle white spaces better.
|
|
|
06-22-2012, 01:17 PM
|
#9
|
Bash Guru
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852
|
Well, I wouldn't have to post it so often it if certain posters would stop suggesting it. Because actually, it doesn't work, at least not consistently.
With loops like the above, the user will find his script breaking at the very first file with spaces in the name. That's why you should always use a while+read loop, preferably with null separators, when working with filename lists.
The safest form of the loop would be this, using bash and gnu grep:
Code:
while IFS='' read -r -d '' file; do
sed -i '/line-width/ c line-width = 7.8' "$file"
done < <( fgrep -lZ "6 \in" * )
Or optionally, you could store the list in an array first, then the for loop would be safe to use, as long as you expanded it with the " [@]" index and quoteed it properly. (notice too how grep is unnecessary)
Code:
files=( * )
for file in "${files[@]}"; do
sed -i '/line-width/ c line-width = 7.8' "$file"
done
You can even bypass the array and just use the globbing form directly as the loop feed.
And as I mentioned, as long as you're using globbing anyway (and the resulting file list) isn't overly huge, you don't even need to use a loop at all. sed can handle multiple file inputs directly.
Code:
sed -i '/line-width/ c line-width = 7.8' *
Last edited by David the H.; 06-22-2012 at 01:22 PM.
|
|
|
06-22-2012, 02:26 PM
|
#10
|
LQ Guru
Registered: May 2005
Location: Atlanta Georgia USA
Distribution: Redhat (RHEL), CentOS, Fedora, CoreOS, Debian, FreeBSD, HP-UX, Solaris, SCO
Posts: 7,831
|
Clearly it DOES work as it least one other person said they did it.
Preferring a number 2 pencil does NOT mean a pen doesn't work.
|
|
|
06-22-2012, 03:20 PM
|
#11
|
Bash Guru
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852
|
That's why I added the caveat "not consistently". It only works when the conditions are exactly right, and the input doesn't contain whitespace or globbing patterns that would force breaks in undesirable locations.
Perhaps your filenames are always clean, but on an open board like this you can't assume that the person you are giving advice to has the same setup. Almost certainly some naive newbie is going to try to use it and wonder why it's not working for him, at the least, if not have some data destroyed because of it.
As long as you, or anyone else, keeps posting such poor code, I'm going to keep correcting it.
(BTW, I expect nothing but the same in return. If I post something that can potentially break things or especially cause data loss, it needs to be corrected, whatever the cost to my own ego.)
Last edited by David the H.; 06-22-2012 at 03:29 PM.
|
|
|
All times are GMT -5. The time now is 08:54 AM.
|
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.
|
Latest Threads
LQ News
|
|