3

This is what I tried:

find . -name *.sv | xargs sed -i -e '<command>' -e '<command>'

It does not work. Using the exact same command on each file still works.

Thanks for the help.

Kusalananda
  • 333,661

3 Answers3

7

To force xargs to only execute sed with a single file at a time, use -n 1 with xargs. You should also pass the pathnames from find using -print0 and get xargs to receive them with -0 (if your find and xargs supports these nonstandard options), so that pathnames containing spaces etc. are handled correctly. You probably also need to specify that you are looking for regular files (and not directories etc.) and you definitely need to quote that shell glob (or it may expand to any matching filename in the current directory):

find . -type f -name '*.sv' -print0 | xargs -0 -n 1 sed -i -e '<command>' -e '<command>'

Or, just use find:

find . -type f -name '*.sv' -exec sed -i -e '<command>' -e '<command>' {} ';'

Related:


A solution that is not using find, but instead uses the ** globbing pattern (matches "recursively" down into subdirectories) available in bash after doing shopt -s globstar:

shopt -s globstar dotglob nullglob
for pathname in ./**/*.sv; do
    if [[ -f "$pathname" ]] && [[ ! -h "$pathname" ]]; then
        sed -i -e '<command> -e '<command>' "$pathname"
    fi
done

This loop loops over all files that have a filename suffix .sv and tests whether each matched name is a regular file (we have to test separately with ! -h to make sure its not a symbolic link to a regular file) before applying the sed command. The shell options enable the use of ** (globstar), matching of hidden names (dotglob), and removes the patterns completely if it does not match (nullglob).

Or, with the zsh shell, which has ** enabled by default, and can use a "glob qualifier" to do most of the "extra" work that bash has to do explicitly:

for pathname in ./**/.sv(.DN); do
    sed -i -e '<command> -e '<command>' "$pathname"
done

The glob qualifier (.DN) modifies the preceding globbing pattern so that it matches only regular files, matches hidden names, and expands to nothing if there is no match at all.

Kusalananda
  • 333,661
  • At least for GNU sed, doesn't -i imply -s (i.e. that sed will treat the files as separate, even if xargs doesn't)? – steeldriver Jun 05 '18 at 19:15
  • @steeldriver Possibly, I don't use GNU sed very often (and never with -i). The original command may still have issues with whitespace in file names though (and the other issues). – Kusalananda Jun 05 '18 at 19:31
  • @Kusalananda I would also recommend combining both of the -e options on the sed. -e denotes a sed script will follow, not a sed command. sed -e '<command1> ; <command2>' FILE works just fine. – Centimane Jun 06 '18 at 10:25
  • @Centimane If you want to get technical about it, -e denotes that the following argument is a sed expression or command. -f is used to read a script from a file. There is no benefit to combining the arguments of several -e into a single string, and in some cases, it's even impossible to do so. – Kusalananda Jun 06 '18 at 11:12
  • @Kusalananda -e denotes a script as per the man page: "add the script to the commands to be executed". I don't think there are any cases where it's impossible, you just might have to escape some characters if using double-quotes instead of single. It is true though that the result is the same, but it's good to recognize that -e is for passing a script rather than a single command. – Centimane Jun 06 '18 at 11:48
  • @Centimane Well, the POSIX spec uses the placeholder script but calls it "the editing commands", so I think we may both call ourselves correct on that point. If you want to use the sed command a or i, it's nice looking to use -e 'i\' -e 'text' than to embed a newline in the single quoted string. Regardless of all of this, whether one or several -e is used (or none) is totally unimportant to this question. – Kusalananda Jun 06 '18 at 12:03
  • Thanks for the solution @Kusalananda. I was wondering what would be the solution if I am not using find to get the filelist that is being fed to xargs. Since the -print0 and -0 options of find and xargs respectively seems to be made for each other. – Prasad Addagarla Oct 29 '19 at 23:00
  • @PrasadAddagarla See updated answer. – Kusalananda Oct 30 '19 at 08:09
2

Here is another method that worked for me:

find -name "*.fail" | xargs -L 1 sed -i -e 's/cse/ecs/g' -e 's/trex/dino/g'
  • This might work for you, but as a generic solution this would fail on files and directories that have newlines in their name. – Kusalananda Oct 30 '19 at 08:10
0

this worked for me grep -rl foo | xargs sed -i 's/foo/bar'

Fuseteam
  • 186