Bryan
Bryan

Reputation: 5471

add filename to beginning of file using find and sed

using the following I add the file name to the front of each line and send the output to a single file.

ls | while read file; do sed -e "s/^/$file/g" $file > out; done

I want to perform the same sed replacement but using a find and exec or xargs command -

find . -type f -exec sed "s/^/{}/g" {} > out +

but I get an error -

find: Only one instance of {} is supported with -exec ... +

Input files are like this -

fileA.txt

A1
A2

fileB.txt

B1
B2

desired output

fileA.txt A1
fileA.txt A2
fileB.txt B1
fileB.txt B2

I know how to do this with awk, but I'd like to do it with sed, find and exec or xargs.

Upvotes: 9

Views: 16447

Answers (5)

Clement
Clement

Reputation: 79

this one works fine for me and it is simpler to use than Kent's answer
NOTE: than the full pathname is inserted for that one

find . -type f | xargs -r -t -i sed -r 's|^|'{}' |g' {}

use this one instead to keep only the bare filename part

find . -type f | xargs -r -t -i sed -r -e 's|^|'{}' |g' -e 's|^.+/||g' {}

then if your are happy with stdout results you might add -i switch to the sed command to overwrite the files

find . -type f | xargs -r -t -i sed -i -r -e 's|^|'{}' |g' -e 's|^.+/||g' {}

Upvotes: 1

Stephen Gross
Stephen Gross

Reputation: 5724

How about:

find . -type f | xargs -i echo FILE/{} > out

Upvotes: 0

Kent
Kent

Reputation: 195209

 find . -type f |xargs awk '$0=FILENAME$0' > out

as I answered this, your "no awk" line not yet there. anyway, take a look my updated answer below:

updated based on comment

so you want to use find, exec/xargs, and sed to do it. My script needs GNU Sed, i hope you have it.

see the one liner first: (well, > out is omitted. You could add it to the end of the line. )

find . -type f | xargs -i echo {}|sed -r 's#(.\/)(.*)#cat &\|sed  "s:^:file \2 :g"#ge'

now let's take a test, see below:

kent$  head *.txt
==> a.txt <==
A1
A2

==> b.txt <==
B1
B2

kent$  find . -type f | xargs -i echo {}|sed -r 's#(.\/)(.*)#cat &\|sed  "s:^:file \2 :g"#ge'
file b.txt B1
file b.txt B2
file a.txt A1
file a.txt A2

is the result your expectation?

Short explanation

  • find ....|xargs -i echo {} nothing to explain, just print the filename per line (with leading "./")
  • then pass the filename to a sed line like sed -r 's#(.\/)(.*)# MAGIC #ge'
  • remember that in the above line, we have two groups \1: "./" and \2 "a.txt"(filename)
  • since we have e at the end of sed line, the MAGIC part would be executed as shell command.(GNU sed needed)
  • MAGIC: cat &\|sed "s:^:file \2 :g cat & is just output the file content, and pipe to another sed. do the replace (s:..:..:g)
  • finally, the execution result of MAGIC would be the Replacement of the outer sed.

the key is the 'e' of Gnu sed.

Upvotes: 6

A.H.
A.H.

Reputation: 66263

Why don't you simply replace the ls in your first line with the find like this?

find . -type f | while read file; do sed -e "s|^|$file|" $file > out; done

You must only exchange the delimiter for s from / to something else not contained in your filenames. I have chosen | as an example.

Upvotes: 1

glenn jackman
glenn jackman

Reputation: 247042

untested, try using xargs

find . -type f | xargs -I FILE sed "s/^/FILE/g" FILE > out

Upvotes: 6

Related Questions