In-place edits with sed on OS X - macos

I'd like edit a file with sed on OS X. I'm using the following command:
sed 's/oldword/newword/' file.txt
The output is sent to the terminal. file.txt is not modified. The changes are saved to file2.txt with this command:
sed 's/oldword/newword/' file1.txt > file2.txt
However I don't want another file. I just want to edit file1.txt. How can I do this?
I've tried the -i flag. This results in the following error:
sed: 1: "file1.txt": invalid command code f

You can use the -i flag correctly by providing it with a suffix to add to the backed-up file. Extending your example:
sed -i.bu 's/oldword/newword/' file1.txt
Will give you two files: one with the name file1.txt that contains the substitution, and one with the name file1.txt.bu that has the original content.
Mildly dangerous
If you want to destructively overwrite the original file, use something like:
sed -i '' 's/oldword/newword/' file1.txt
^ note the space
Because of the way the line gets parsed, a space is required between the option flag and its argument because the argument is zero-length.
Other than possibly trashing your original, I’m not aware of any further dangers of tricking sed this way. It should be noted, however, that if this invocation of sed is part of a script, The Unix Way™ would (IMHO) be to use sed non-destructively, test that it exited cleanly, and only then remove the extraneous file.

I've similar problem with MacOS
sed -i '' 's/oldword/newword/' file1.txt
doesn't works, but
sed -i"any_symbol" 's/oldword/newword/' file1.txt
works well.

The -i flag probably doesn't work for you, because you followed an example for GNU sed while macOS uses BSD sed and they have a slightly different syntax.
All the other answers tell you how to correct the syntax to work with BSD sed. The alternative is to install GNU sed on your macOS with:
brew install gsed
and then use it instead of the sed version shipped with macOS (note the g prefix), e.g:
gsed -i 's/oldword/newword/' file1.txt
If you want GNU sed commands to be always portable to your macOS, you could prepend "gnubin" directory to your path, by adding something like this to your .bashrc/.zshrc file (run brew info gsed to see what exactly you need to do):
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
and from then on the GNU sed becomes your default sed and you can simply run:
sed -i 's/oldword/newword/' file1.txt

sed -i -- "s/https/http/g" file.txt

You can use -i'' (--in-place) for sed as already suggested. See: The -i in-place argument, however note that -i option is non-standard FreeBSD extensions and may not be available on other operating systems. Secondly sed is a Stream EDitor, not a file editor.
Alternative way is to use built-in substitution in Vim Ex mode, like:
$ ex +%s/foo/bar/g -scwq file.txt
and for multiple-files:
$ ex +'bufdo!%s/foo/bar/g' -scxa *.*
To edit all files recursively you can use **/*.* if shell supports that (enable by shopt -s globstar).
Another way is to use gawk and its new "inplace" extension such as:
$ gawk -i inplace '{ gsub(/foo/, "bar") }; { print }' file1

This creates backup files. E.g. sed -i -e 's/hello/hello world/' testfile for me, creates a backup file, testfile-e, in the same dir.

You can use:
sed -i -e 's/<string-to-find>/<string-to-replace>/' <your-file-path>
Example:
sed -i -e 's/Hello/Bye/' file.txt
This works flawless in Mac.

If you need to substitute more than one different words:
sed -i '' -e 's/_tools/tools/' -e 's/_static/static/' test.txt

Related

sed doesn't catch all sets of doubles

I've writted a sed script to replace all ^^ with NULL. It seems though that sed is only catching a pair, but not including the second in that pair as it continues to search.
echo "^^^^" | sed 's/\^\^/\^NULL\^/g'
produces
^NULL^^NULL^
when it should produce
^NULL^NULL^NULL^
Try with a loop to apply your command again to modified pattern space:
echo "^^^^" | sed ':a;s/\^\^/\^NULL\^/;t a;'
To edit a file in place on OSX, try the -i flag and multiline command:
sed -i '' ':a
s/\^\^/\^NULL\^/
t a' file
With GNU sed:
sed -i ':a;s/\^\^/\^NULL\^/;t a;' file
or simply redirect the command to a temporary file before renaming it:
sed ':a;s/\^\^/\^NULL\^/;t a;' file > tmp && mv tmp file
I really like SLePort solution, but since it is not working for you, you can try with (tested on Linux, not Mac):
echo "^^^^" | sed 's/\^\^/\^NULL\^/g; s//\^NULL\^/g'
It is doing the same as the former solution, but explicitly, not looping with tags.
You can omit the pattern in the second command and sed will use the previous pattern.

Removing lines from multiple files with sed command

So, disclaimer: I am pretty new to using bash and zsh, so there is a chance the answer is really simple. Nonetheless. I checked previous postings and couldn't find anything. (edit: I have tried this in both bash and zsh shells- same problem.)
I have a directory with many files and am trying to remove the first line from each file.
So say the directory contains: file1.txt file2.txt file3.txt ... etc.
I am using the sed command (non-GNU):
sed -i -e "1d" *.txt
For some reason, this is only removing the first line of the first file. I thought that the *.txt would affect all files matching the pattern in directory. Strangely, it is creating the file duplicates with -e appended, but both the duplicate and original are the same.
I tried this with other commands (e.g. ls *.txt) and it works fine. Is there something about sed I am missing?
Thank you in advance.
Different versions of sed in differing operating systems support various parameters.
OpenBSD (5.4) sed
The -i flag is unavailable. You can use the following /bin/sh syntax:
for i in *.txt
do
f=`mktemp -p .`
sed -e "1d" "${i}" > "${f}" && mv -- "${f}" "${i}"
done
FreeBSD (11-CURRENT) sed
The -i flag requires an extension, even if it's empty. Thus must be written as sed -i "" -e "1d" *.txt
GNU sed
This looks to see if the argument following -i is another option (or possibly a command). If so, it assumes an in-place modification. If it appears to be a file extension such as ".bak", it will rename the original with the ".bak" and then modify it into the original file's name.
There might be other variations on other platforms, but those are the three I have at hand.
use it without -e !
for one file use:
sed -i '1d' filename
for all files use :
sed -i '1d' *.txt
or
files=/path/to/files/*.extension ; for var in $files ; do sed -i '1d' $var ; done
.for me i use ubuntu and debian based systems , this method is working for me 100% , but for other platformes i'm not sure , so this is other method :
replace first line with emty pattern , and remove empty lines , (double commands):
for files in $(ls /path/to/files/*.txt); do sed -i "s/$(head -1 "$files")//g" "$files" ; sed -i '/^$/d' "$files" ; done
Note: if your files contain splash '/' , then it will give error , so in this case sed command should look like this ( sed -i "s[$(head -1 "$files")[[g" )
hope that's what you're looking for :)
The issue here is that the line number isn't reset when sed opens a new file, so 1 only matches the first line of the first file.
One solution is to use a shell loop, calling sed once for each file. Gumnos' answer shows how to do this in the most widely compatible way, although if you have a version of sed supporting the -i flag, you could do this instead:
for i in *.txt; do
sed -i.bak '1d' "$i"
done
It is possible to avoid creating the backup file by passing an empty suffix but personally, I don't think it's such a bad thing. One day you'll be grateful for it!
It appears that you're not working with GNU tools but if you were, I would recommend using GNU awk for this task. The variable FNR is useful here, as it keeps track of the record number for each file individually, allowing you to do this:
gawk -i inplace 'FNR>1' *.txt
Using the inplace extension, this allows you to remove the first line from each of your files, by only printing the lines where FNR is greater than 1.
Testing it out:
$ seq 5 > file1
$ seq 5 > file2
$ gawk -i inplace 'FNR>1' file1 file2
$ cat file1
2
3
4
5
$ cat file2
2
3
4
5
The last argument you are passing to the Sed is the problem
try something like this.
var=(`find *txt`)
for file in "${var[#]}"
do
sed -i -e 1d $file
done
This did the trick for me.

sed: urecognized option -- i

I'm using Window 7.
sed -V
gives me
GNU sed version 3.02
I have a text file with:
foo bar
In command line if I write:
sed s/foo/tofu/ test.txt
I get printed on stdout;
tofu bar
However, I want to edit this file inplace, so using the -i option:
sed -i s/foo/tofu/ test.txt
gives me sed: invalid option -- i
What am I missing?
While I can't guarantee this will work because I'm not running it on Windows 7 but have you considered using redirection and then swapping?
Meaning something like this:
sed s/foo/tofu/ test.txt > tmp.txt && mv tmp.txt test.txt
While this is a very dirty way of doing things I think it might work. You can also try doing
sed s/foo/tofu/ test.txt > test.txt
but I feel that might lead to some errors because you're reading and writing on the same file.
It's the sed version. Try the one found here:
http://unxutils.sourceforge.net/UnxUpdates.zip

sed command not working in mac

Following sed command is not working on my lion mac.
find . -type f -exec sed -i 's/user_dashboard/user/g' {} \;
I am getting this error
sed: 1: "./vendor/assets/javascr ...": invalid command code .
The OSX version of sed is not the same as those found in most Linux systems.
It extends the -i option to give you the opportunity save a file with a different extension, but requires input for that extension.
If you just want to overwrite the file in place, you need to use sed -i "" ...sedCmd.... fileName to rename your file in-place.
Per #JamesMcMahon 's comment, see here for the full doc for OSX/BSD sed.
I hope this helps.
-i probably has a different meaning (not "in-place") in your version of sed. Try using gsed if available or replacing -i with -e and using a temporary file (and a mv afterwards) to emulate it.
replacing text inside a file on the fly with sed on mac is possible,
the command is just a little different.
with: -i , you have to specify a postfix, which sed will use to save the original file after it modified it.
run the command as:
$ sed -i _backup -E 's/foo/bar/' /tmp/jestinkt.txt
ending with both the modified /tmp/jestinkt.txt and the original /tmp/jestinkt.txt_backup

unix commandline for inline replacement of all newlines in file with <br>\n

sed 's/$/<br>/' mytext.txt
worked but output it all on the command line. I want it to just do the replacement WITHIN the specified file itself. Should I be using another tool?
If you have gnu sed, you can use the -i option, which will do the replacement in place.
sed -i 's/$/<br>/' mytext.txt
Otherwise you will have to redirect to another file and rename it over the old one.
sed 's/$/<br>/' mytext.txt > mytext.txt.new && mv mytext.txt.new mytext.txt
Just for completeness. On Mac OS X (which uses FreeBSD sed) you have to use an additional null-string "" for in-place file editing without backup:
sed -i "" 's/$/<br>/' mytext.txt
As an alternative to using sed for no-backup in-place file editing, you may use ed(1), which, however, reads the entire file into memory before operating on it.
printf '%s\n' H 'g/$/s//<br>/g' ',p' | ed -s test.file # print to stdout
printf '%s\n' H 'g/$/s//<br>/g' wq | ed -s test.file # in-place file edit
For more information on ed(1) see:
"Editing files with the ed text editor from scripts",
http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed
If you have an up-to-date sed, just use sed -i or sed --in-place, which will modify the actual file itself.
If you want a backup, you need to supply a suffix for it. So sed -i.bak or sed --in-place=.bak will create a backup file with the .bak suffix.
Use this! Seriously! You'll appreciate it a lot the first time you damage your file due to a mistyped sed command or wrong assumption about the data in the file.
Use the redirection symbol i.e.
sed 's/$/<br>/' mytext.txt > mytext2.txt && mv mytext2.txt mytext.txt

Resources