3

I am using GNU sed 4.2.2 and after searching cannot find out the reason sed behaves oddly in some situations:

I have a directory with the following:

foofile.txt
barfile.txt
bazfile.txt
config/

Case 1

sed -i 's/foo/bar/g' *.txt

This works as I expect. It replaces all the "foo"s with "bar"s in the three regular files.

Case 2

sed -i 's/foo/bar/g' *
sed: couldn't edit config: not a regular file

It replaces "foo" with "bar" in barfile.txt and bazfile.txt, but not in foofile.txt. I assume it goes through the list of files expanded from the * alphabetically, and when it hits config/ it errors and exits. Is there a way to have sed ignore errors and continue processing files?

Case 3

for file in $(find . -maxdepth 1 -type f); do sed -i 's/foo/bar/g' <"$file"; done
sed: no input files
sed: no input files
sed: no input files

Could someone please explain why sed does this? Why does it say there's no input file when it's being given one?

I know that I can use the following but I'm asking why sed acts this way, not how do I solve this one use case.

find . -maxdepth 1 -type f -exec sed -i 's/foo/bar/g' {} \;
4

It's normal behavior. In both cases sed exits with error code 4... per info sed:

4
     An I/O error, or a serious processing error during runtime,
     GNU 'sed' aborted immediately.

and in both cases the messages are self-explanatory. Not sure what's unclear but for the record: the first time it errors out because it cannot edit a directory and the second time it complains because it cannot edit stdin in-place, it needs a file (i.e. remove that redirect before $file)
The proper way to do this with find is, as you noted, via -exec ...
With globs, you'll have to use a loop and test if input is a regular file before running sed. Or, if you're a zsh user, you can simply do:

sed -i 's/foo/bar/g' *(.)
  • I think I understand how Case 3 is working now thank you. Do you know how to make sed continue even if it encounters an error like in Case 2? – Linoob Apr 6 '17 at 21:26
  • @Linoob - no; I think the man page is pretty clear: "GNU 'sed' aborts immediately" – don_crissti Apr 6 '17 at 21:27
2
  • Case 2:

    Avoid that directory with find:

    sed -i 's/foo/bar/g' `find . -maxdepth 1 -type f`
    
  • Case 3:

    The problem is the <"$file" in the loop, that turns the file into a stream so sed never sees a filename. Just remove that <:

    for file in $(find . -maxdepth 1 -type f); do sed -i 's/foo/bar/g' "$file"; done
    

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.