sed over multiple files in multiple directories - sed

I have the following directory tree:
books>book(i)>cluster.pir
where book(i) are a set of sub directories 1 to 1023 each containing a folder called cluster.pir.
The following sed command:
sed -i '/>/d' ./*.pir
will delete any line in the file containing '>' for any file with a .pir ext, which is great, but my various .pir files are located in their own book(i) directory. How do I get the command to span across all the directories? I have tried:
find ./*.pir -type f -exec sed -i '/>/d' ./*.pir
when starting in the 'book' parent directory, but I get:
find: missing argument to `-exec'
does anyone have any thoughts on this?
Thanks.

The format for find is:
find -exec command {} \;
Where {} is replaced by the filename.
Edit: In your case this would become:
find ./*.pir -type f -exec sed -i '/>/d' {} \;
This will call sed on every file.

You can add a wildcard to span all directories:
sed -i '/>/d' ./book*/*.pir

I was having trouble using file wild-cards with sed on my Mac and this method worked fine:
FILE_PATH="/some/path/"
sed -i '' "s|search|replace|g" $(find ${FILE_PATH} -name '*.ext')

Related

No such file or directory when using sed in combination with find?

I am trying to run a script which corrects line breaks in all PHP files. This is how it looks like:
find . -type f -name \*.php -print -exec sed -i '' 's/\r//' {} \;
The errors I get for all files is:
./downloader/lib/Mage/Backup/Exception/SomeFile.php sed: can't read
s/\r//: No such file or directory
Whats the error in the script?
Thanks
The problem is on -i, it depends on the OS you use.
sed -i '' 's/\r//' {}
On macOS, sed expects the file extension to use for the backup file as a separate argument, following -i. An empty string, as you use, tells it to not create a backup file.
On Linux, sed expects the file extension to be joined together with -i in a single argument. For example, -i.bak to append .bak to the file name to generate the name of the backup file or -i to not create a backup file.
Since you get an error that says that the file 's/\r//' does not exist it means that you are using Linux and '' is interpreted as the sed program.
Remove '' and it should work:
sed -i 's/\r//' {}
You can do the following:
find . -type f -name \*.php -print -exec sed -i 's/\r//' {} \;
The issue is sed is expecting sed -i <substitution_pattern> <file>. In your incantation, the '' is interpreted as the substitution pattern, then the 's/\r//' is being interpreted as the file

Sed * only modifying first file

I would like to delete the first 40 lines of a good number of ASCII files and save the ASCII files without those 40 lines
I'm working under OSX High Sierra, realized that the -i option in sed was not working unless I create a backup file, so I tried using this command:
sed -i'backup' -e '1,40d' *.txt
It however only modifies and deletes the first 40 lines in my first file (alphabetically), but not the others.
How can I edit multiple files with just one command?
Thanks
You can use the following command that will
look in the current folder
ignore sub folder
take only into account files whose filenames end with '*.txt'
before executing the sed command.
Command:
find . -maxdepth 1 -type f -name '*.txt' -exec sed -i 'backup' -e '1,40d' {} \;

sed not working properly with multiple input files

sed -i is creating a backup of all files in subdirectories before editing in place (as expected) but it's not actually editing files in subdirectories.
$ mkdir -p a/b
$ echo "A" > a/a.txt
$ echo "B" > a/b/b.txt
Now I have two text files, one in a one in a subdirectory of a
$ sed -i.bac "1s/^/PREPENDED /" a/**/*.txt
Backups are created for both:
$ find a
a
a/a.txt
a/a.txt.bac
a/b
a/b/b.txt
a/b/b.txt.bac
Only a.txt is edited:
$ cat a/a.txt
PREPENDED A
$ cat a/b/b.txt
B
I'm using ZSH (so I have globstar support) and I'm on Mac.
Why is this happening and how can I fix it?
It's happening because your sed invocation only has a single line 1, which happens to be in a.txt. If you want it to do it for each file then you need to invoke sed multiple times.
for f in a/**/*.txt
do
sed ... "$f"
done
Since you are needing to descend through several levels of directories, a single invocation of sed alone is not sufficient. However, using find you can accomplish what you want in a single line. If you are not familiar with find ... -exec '{}' \; it is worth taking a few minutes with startpage.com and do a quick search. In your case, the following invocation works well:
find a -type f -name "*.txt" -exec sed -i.bac 's/^/PREPENDED /' '{}' \;
Here find searches directory a and all below for any file (-type f) matching *.txt, then for each file (indicated by '{}') -exec executes sed -i.bac 's/^/PREPENDED /' and lastly an escaped \; is given to indicate the end of the -exec command.
results:
$ ls -1 a
b
a.txt
a.txt.bac
$ ls -1 a/b
b.txt
b.txt.bac
$ cat a/a.txt
PREPENDED A
$ cat a/b/b.txt
PREPENDED B
As was correctly pointed out, with globstar set shopt -s globstar it is unnecessary to use find as the following invocation of sed is sufficient:
sed -i.bac 's/^/PREPENDED /' a/**/*.txt

Using sed to find and delete across multiple files recursively

How can I do a string match against, for example:
<meta name="keywords" content="
Then delete that whole line every time a match is found?
I'm looking to do this for all files in the current directory and below.
I'm also new to sed.
Try this command:
find . -type f -exec sed -i '/foobar/d' {} \;
Change foobar to what you search for.
In answer to the question: "How do I do x to all files recursively?", the answer is to use find. To use sed to delete a line, you can either use the non-portable -i, or simply write a script to redirect the stream. For example:
find . -exec sh -c 'f=/tmp/t.$$;
sed "/<meta name=\"keywords\" content=\"/d" $0 > $f; mv $f $0' {} \;

sed command to write the name of file to HTML comment

I'm looking for a sed command that, with find, I can take a directory tree of JSP files and write the name of the file in an HTML comment to the top of the file.
This will allow me to review a legacy application JSP call tree of in the HTML source.
I'm thinking it will be a one liner for a talented sed guru...
something like:
find . -name '.jsp' -exec sed ? ? ? {} \;
Maybe something using xargs is more appropriate, but I think sed is the tool that will do the work.
If you want to use sed, you can try
find -name "*.jsp" -exec sed -i '1i <!-- {} -->' {} \;
Works fine for me in the presence of /.
On Unix the filename will contain slashes (/) which are special characters for sed, so I would recommend this simpler approach that writes the filename at the bottom of the file:
find . -name '*.jsp' -exec sh -c "echo '<\!-- {} -->' >> '{}'" \;
To write the filename at the top of the file use this:
find . -name '*.jsp' -exec sh -c \
'echo "<!-- {} -->" > "{}.new" && cat "{}" >> "{}.new" && mv "{}.new" "{}"' \;
N.B. The filename might contain characters that might render your HTML invalid, e.g. &, although I doubt that a JSP could have such a strange name.