Passing regular files only to `sed -i`

13,950

Solution 1

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' *(.)

Solution 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
    
Share:
13,950

Related videos on Youtube

Linoob
Author by

Linoob

Updated on September 18, 2022

Comments

  • Linoob
    Linoob over 1 year

    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' {} \;
    
  • Linoob
    Linoob about 7 years
    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?
  • don_crissti
    don_crissti about 7 years
    @Linoob - no; I think the man page is pretty clear: "GNU 'sed' aborts immediately"